package xutil

import (
	"encoding/json"
	"errors"
	"fmt"
	"sync"
	"time"

	"gitee.com/wosylf/ltool"
	"gitee.com/wosylf/ltool/pkg/util"
	"gitee.com/wosylf/ltool/pkg/xhttp"
)

type BaiduToken struct {
	AccessToken string ` json:"access_token" form:"access_token"`
	ExpiresIn   int64  ` json:"expires_in" form:"expires_in"` //过期时间

}

type UserCard struct {
	Name      string `gorm:"type:varchar(8);comment:'真实姓名'" json:"name" form:"name"`
	FacadeUrl string `gorm:"type:varchar(128);comment:'身份证正面地址'" json:"facade_url,omitempty" form:"facade_url"`
	BackUrl   string `gorm:"type:varchar(128);comment:'身份证反面地址'" json:"back_url,omitempty" form:"back_url"`
	Cardid    string `gorm:"type:char(18);comment:'身份证号';index" json:"cardid" form:"cardid"` //身份证号
	Md5cardid string `gorm:"-" json:"-" form:"-"`                                            //身份证号
	Address   string `gorm:"type:varchar(26);comment:'住址'" json:"address" form:"address"`    //住址
	Birthday  string `gorm:"type:char(8);comment:'出生'" json:"birthday" form:"birthday"`      //生日
	Sex       string `gorm:"type:char(1);comment:'性别'" json:"sex" form:"sex"`                //性别
	Nation    string `gorm:"type:varchar(5);comment:'民族'" json:"nation" form:"nation"`       //民族
	Issue     string `gorm:"type:char(8);comment:'签发日期'" json:"issue" form:"issue"`          //签发日期
	Invalid   string `gorm:"type:char(8);comment:'失效日期'" json:"invalid" form:"invalid"`      //失效日期
	Section   string `gorm:"type:varchar(16);comment:'签发机关'" json:"section" form:"section"`  //签发机关
	Age       uint8  `gorm:"comment:'年龄'" json:"age" form:"age"`                             //失效日期
	Surface   string `gorm:"-" json:"-" form:"-"`                                            //是正面还是反面
}

type BaiduCard struct {
	ImageStatus string                 ` json:"image_status" form:"image_status"` //图片状态
	ErrorCode   int                    ` json:"error_code" form:"error_code"`     //图片状态
	RiskType    string                 ` json:"risk_type" form:"risk_type"`       //风险类型
	EditTool    string                 ` json:"edit_tool" form:"edit_tool"`       //用啥软件编辑过图片
	WordsResult map[string]WordsResult ` json:"words_result" form:"words_result"` //风险类型
	CardQuality CardQuality            ` json:"card_quality" form:"card_quality"` //风险类型
}

type CardQuality struct {
	IsClear_propobility    float64 ` json:"IsClear_propobility" form:"IsClear_propobility"`
	IsClear                float64 ` json:"IsClear" form:"IsClear"` //是否清晰
	IsNoCover_propobility  float64 ` json:"IsNoCover_propobility" form:"IsNoCover_propobility"`
	IsNoCover              float64 ` json:"IsNoCover" form:"IsNoCover"` //是否边框/四角完整
	IsComplete_propobility float64 ` json:"IsComplete_propobility" form:"IsComplete_propobility"`
	IsComplete             float64 ` json:"IsComplete" form:"IsComplete"` //是否头像、关键字段无遮挡/马赛克
}

type WordsResult struct {
	Location Location ` json:"location" form:"location"`
	Words    string   ` json:"words" form:"words"`
}

type Location struct {
	Top    float64 ` json:"top" form:"top"`
	Left   float64 ` json:"left" form:"left"`
	Width  float64 ` json:"width" form:"width"`
	Height float64 ` json:"height" form:"height"`
}

const (
	百度获取token地址 = "https://aip.baidubce.com/ouc/2.0/token?grant_type=client_credentials&client_id=%v&client_secret=%v"
	百度身份识别地址    = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token="
	百度识别正面      = "front"
	百度识别反面      = "back"
	V身份证签发日期    = "签发日期"
	V身份证失效日期    = "失效日期"
	V身份证签发机关    = "签发机关"
	V身份证姓名      = "姓名"
	V身份证民族      = "民族"
	V身份证住址      = "住址"
	V身份证身份号     = "公民身份号码"
	V身份证生日      = "出生"
	V身份证性别      = "性别"
)

var (
	身份证风险数组 = map[string]string{
		"copy":      "识别异常，请拍身份证重新上传",
		"temporary": "不能为临时身份证，请拍身份证上传",
		"screen":    "不能为翻拍照片",
		"unknown":   "照片异常",
	}
	身份证状态数组 = map[string]string{
		"non_idcard":      "上传的图片中不包含身份证",
		"blurred":         "身份证模糊，请重新上传",
		"other_type_card": "其他类型证照，请上传身份证照片",
		"over_exposure":   "身份证关键字段反光或过曝，请调整亮度重新上传",
		"over_dark":       "身份证欠曝，请拍亮一些",
		"unknown":         "照片异常,请重试",
	}
)

type FBaidu struct {
	Ak  string
	Sk  string
	Tk  string
	Exp int64
	M   sync.RWMutex
}

func F新建百度识别(ak, sk string) *FBaidu {
	return &FBaidu{Ak: ak, Sk: sk}
}

func (a *FBaidu) F获取缓存token() (token string, err error) {
	//先看缓存有没有
	a.M.RLock()
	if a.Tk != "" && a.Exp > time.Now().Unix() {
		a.M.RUnlock()
		return a.Tk, nil
	} else {
		a.M.RUnlock()
	}
	a.M.Lock()
	a.Tk, a.Exp, err = a.F获取token()
	a.M.Unlock()
	//bm["access_token"]
	return
}

func (a *FBaidu) F获取Tk与过期时间和锁() (string, int64, *sync.RWMutex) {
	return a.Tk, a.Exp, &a.M
}
func (a *FBaidu) F获取token() (token string, exp int64, err error) {
	//先看缓存有没有
	url := fmt.Sprintf(百度获取token地址, a.Ak, a.Sk)
	_, by, err := xhttp.NewClient().Get(url).EndBytes(ctx)
	if err != nil {
		return
	}
	var bm BaiduToken
	if err = json.Unmarshal(by, &bm); err != nil {
		return
	}
	if bm.AccessToken == "" {
		err = errors.New("获取百度token失败")
		return
	}
	a.Tk, a.Exp = bm.AccessToken, bm.ExpiresIn+time.Now().Unix()-60
	return a.Tk, a.Exp, nil
}

// 第一个为路径，第二个为正反面,默认正面
func (a *FBaidu) F身份证识别(args ...string) (uc *UserCard, err error) {
	token, err := util.F获取缓存token(a)
	if err != nil {
		return
	}
	path := args[0]
	正反面 := 百度识别正面
	if len(args) > 1 {
		正反面 = args[1]
	}
	var bm1 BaiduCard
	url := 百度身份识别地址 + token
	bm := make(ltool.BodyMap)
	bm.
		Set("url", path).
		Set("id_card_side", 正反面).
		Set("detect_risk", "true").
		Set("detect_quality", "true")
	//Set("detect_card","true")//是否对身份证进行裁剪，暂时先不裁剪
	encodeurl := bm.EncodeURLParams()
	_, err = xhttp.NewClient().Type(xhttp.TypeFormData).Post(url).SendString(encodeurl).EndStruct(ctx, &bm1)
	if err != nil {
		return
	}
	if bm1.ImageStatus != "normal" && bm1.ImageStatus != "reversed_side" {
		err = errors.New(身份证状态数组[bm1.ImageStatus])
		return
	}
	if bm1.EditTool != "" {
		err = errors.New("身份证被剪辑过，请直接拍照上传")
		//这里上传的身份证被编辑过，可以加一道风控
		return
	}
	if bm1.RiskType != "normal" && bm1.RiskType != "" {
		err = errors.New(身份证风险数组[bm1.RiskType])
		return
	}
	if _, ok := bm1.WordsResult[V身份证签发日期]; ok {
		// 存在
		正反面 = 百度识别反面
	}
	uc = new(UserCard)
	if 正反面 == 百度识别反面 || (bm1.ImageStatus == "reversed_side" && 正反面 != 百度识别反面) {
		//plug.Println("反面")
		newt := StrToInt(time.Now().Format("20060102")) //当前时间
		uc.Issue = bm1.WordsResult[V身份证签发日期].Words
		uc.Invalid = bm1.WordsResult[V身份证失效日期].Words
		uc.Section = bm1.WordsResult[V身份证签发机关].Words
		uc.BackUrl = path
		if newt < StrToInt(uc.Issue) {
			err = errors.New("签发日期异常")
			return
		}
		if newt > StrToInt(uc.Invalid) {
			err = errors.New("身份证已过期")
			return
		}
		//uc.Age = uint8(newt - StrToInt(uc.Birthday)/10000)
	} else {
		//plug.Println("正面")
		uc.FacadeUrl = path
		uc.Birthday = bm1.WordsResult[V身份证生日].Words
		uc.Name = bm1.WordsResult[V身份证姓名].Words
		uc.Sex = bm1.WordsResult[V身份证性别].Words
		uc.Cardid = bm1.WordsResult[V身份证身份号].Words
		uc.Age = uint8(F生日转年龄(bm1.WordsResult[V身份证生日].Words))
		uc.Nation = bm1.WordsResult[V身份证民族].Words
		uc.Address = bm1.WordsResult[V身份证住址].Words

	}
	uc.Surface = 正反面
	return
}
